package com.example.utils;

import com.example.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

import static com.example.common.StatusCodes.FORMAT_ERROR_MSG;

/**
 * Author: Zeran
 * Date: 2024-06-17 0:17
 * Desc: 通用工具类
 */
@Slf4j
@Component
public class CommonUtil {
	// 保存文件的根目录
	private static final String UPLOAD_DIR = "static/uploads/";

	@Value("${server.address}")
	private String serverAddress;
	@Value("${server.port}")
	private String serverPort;

	/**
	 * 验证用户名和密码格式
	 *
	 * @param username 用户名
	 * @param password 密码
	 */
	public static void isValid(String username, String password) {
		// 正则匹配
		String regxUsername = "^[a-zA-Z0-9_]{4,10}$";
		String regxPassword = "^[a-zA-Z0-9_]{6,12}$";
		if (!username.matches(regxUsername)) {
			throw new ServiceException(FORMAT_ERROR_MSG + "!\n用户名格式不正确，长度为4-10位字母数字下划线");
		}
		if (!password.matches(regxPassword)) {
			throw new ServiceException(FORMAT_ERROR_MSG + "!\n密码格式不正确，格式为6-12位字母数字下划线");
		}
	}

	/**
	 * 将字节数组保存为图片文件
	 *
	 * @param bytes    字节流数据
	 * @param filePath 文件路径
	 */
	public static void saveImageToLocal(byte[] bytes, String filePath) {
		// 确保目标目录存在
		File directory = new File(filePath).getParentFile();
		if (!directory.exists()) {
			log.error("目录{}不存在", directory.getAbsolutePath());
			return;
		}
		// 将字节流写入文件
		try (FileOutputStream fos = new FileOutputStream(filePath)) {
			fos.write(bytes);
		} catch (IOException e) {
			log.error("文件写入失败", e);
		}
	}

	/**
	 * 将LocalDateTime转成国内时间戳
	 *
	 * @param time LocalDateTime
	 * @return 时间戳
	 */
	public static Long convertLocalDateTimeToTimestamp(LocalDateTime time) {
		return time.atZone(ZoneId.of("Asia/Shanghai"))
				.toInstant()
				.toEpochMilli();
	}

	/**
	 * 将 Base64 数据转换为 MultipartFile
	 *
	 * @param base64String Base64 字符串（带或不带前缀）
	 * @param fileName     转换后的文件名
	 * @return MultipartFile 对象
	 * @throws IOException 当解码或流操作失败时抛出
	 */
	public static MultipartFile convertBase64ToMultipartFile(String base64String, String fileName) {
		try {
			// 去掉 Base64 前缀
			if (base64String.startsWith("data:image/")) {
				base64String = base64String.substring(base64String.indexOf(",") + 1);
			}

			// 解码 Base64
			byte[] decodedBytes = Base64.getDecoder().decode(base64String);

			// 检查解码后的字节数组
			if (decodedBytes.length == 0) {
				throw new ServiceException("解码后的字节数组为空");
			}

			// 创建 MultipartFile
			return new MockMultipartFile(fileName, fileName, "image/jpeg", decodedBytes);
		} catch (IllegalArgumentException e) {
			throw new ServiceException("Base64 解码失败", e);
		}
	}


	/**
	 * 上传文件
	 *
	 * @param file    MultipartFile文件
	 * @param subPath 子路径
	 * @return 返回图片在服务器的地址
	 */
	public Map<String, String> uploadFile(MultipartFile file, String subPath) {
		// 获取文件名
		String fileName = file.getOriginalFilename();
		if (fileName == null || fileName.isEmpty()) {
			throw new ServiceException("文件名不能为空");
		}

		try {
			// 计算文件保存路径
			Path targetPath = Paths.get(System.getProperty("user.dir"), "src/main/resources", UPLOAD_DIR, subPath,
					fileName);
			File directory = targetPath.getParent().toFile();

			// 确保目标目录存在
			if (!directory.exists()) {
				Files.createDirectories(directory.toPath());
			}

			// 保存文件到本地
			file.transferTo(targetPath.toFile());

			// 生成文件访问 URL
			String fileUrl = "http://" + serverAddress + ":" + serverPort + "/uploads" + subPath + fileName;
			// 相对路径 (用于存储到数据库)
			String dbPath = subPath + fileName;

			// 构建返回结果
			Map<String, String> result = new HashMap<>();
			result.put("fileUrl", fileUrl); // 文件在服务器的访问地址
			result.put("dbPath", dbPath);  // 文件存储在数据库中的路径

			// 返回文件访问路径
			return result;
		} catch (IOException e) {
			throw new ServiceException("文件上传失败：" + e.getMessage());
		}

	}
}
